package Common;
import java.util.Map;
import java.util.HashMap;

/**
 *
 * @author cantwelk
 */
public class Instruction {

    public static class Format
    {
        public static final int FUNCTION = 1;
        public static final int RLABEL = 1 << 1;
        public static final int LITERAL = 1 << 2;
        public static final int NN = 1 << 3;
        public static final int NMAX = 1 << 4;
        public static final int MLABEL = 1 << 5;
        public static final int NONE = 1 << 6;
        public static final int EXPRESSION = 1 << 7;
        public static final int DUMP = 1 << 8;
        public static final int TEST = 1 << 9;
        public static final int NNNNNN = 1 << 10;
        public static final int DAT = 1 << 11;
        public static final int OLABEL = NONE | RLABEL;
    }
    private static final Map<String,Operator> op_table = new HashMap<String,Operator>();

    public static enum Type{DIRECTIVE,OPERATOR,ERROR}

    public static String[] TestPrefix = {"=", "^=", "<", ">", "<=", ">="};

    static{
        //CNTL operator and all its usable function
        op_table.put("CNTL", new Operator("CNTL", 0, Format.FUNCTION ,new HashMap<String,Function>()));
        op_table.get("CNTL").Functions.put("HALT", new Function("HALT",0, Format.NMAX));
        op_table.get("CNTL").Functions.put("DUMP", new Function("DUMP",1, Format.DUMP));
        op_table.get("CNTL").Functions.put("CLRD", new Function("CLRD",2, Format.NONE));
        op_table.get("CNTL").Functions.put("CLRT", new Function("CLRT",3, Format.NONE));
        op_table.get("CNTL").Functions.put("GOTO", new Function("GOTO",4, Format.RLABEL | Format.NMAX));

        //STACK operator...
        op_table.put("STACK", new Operator("STACK", 0,Format.FUNCTION, new HashMap<String,Function>()));
        op_table.get("STACK").Functions.put("PUSH", new Function("PUSH",5, Format.RLABEL | Format.NMAX | Format.LITERAL));
        op_table.get("STACK").Functions.put("POP", new Function("POP",6, Format.RLABEL | Format.NMAX));
        op_table.get("STACK").Functions.put("TEST", new Function("TEST",7, Format.RLABEL | Format.NMAX | Format.LITERAL));

        //JUMP operator...
        op_table.put("JUMP", new Operator("JUMP", 1, Format.RLABEL | Format.NMAX, new HashMap<String,Function>()));
        op_table.get("JUMP").Functions.put("=", new Function("=",0));
        op_table.get("JUMP").Functions.put("^=", new Function("^=",1));
        op_table.get("JUMP").Functions.put("<", new Function("<",2));
        op_table.get("JUMP").Functions.put(">", new Function(">",3));
        op_table.get("JUMP").Functions.put("<=", new Function("<=",4));
        op_table.get("JUMP").Functions.put(">=", new Function(">=",5));
        op_table.get("JUMP").Functions.put("TNULL", new Function("tnull",6));
        op_table.get("JUMP").Functions.put("DNULL", new Function("dnull",7));


        //SOPER operation...
        op_table.put("SOPER", new Operator("SOPER", 2, Format.NN | Format.RLABEL | Format.NMAX, new HashMap<String,Function>()));
        op_table.get("SOPER").Functions.put("ADD", new Function("ADD",0));
        op_table.get("SOPER").Functions.put("SUB", new Function("SUB",1));
        op_table.get("SOPER").Functions.put("MUL", new Function("MUL",2));
        op_table.get("SOPER").Functions.put("DIV", new Function("DIV",3));
        op_table.get("SOPER").Functions.put("OR", new Function("OR",4));
        op_table.get("SOPER").Functions.put("AND", new Function("AND",5));
        op_table.get("SOPER").Functions.put("READN", new Function("READN",6));
        op_table.get("SOPER").Functions.put("READC", new Function("READC",6));
        op_table.get("SOPER").Functions.put("WRITEN", new Function("WRITEN",7));
        op_table.get("SOPER").Functions.put("WRITEC", new Function("WRITEC",7));


        //MOPER operation...
        op_table.put("MOPER", new Operator("MOPER", 3, Format.RLABEL | Format.NMAX | Format.LITERAL, new HashMap<String,Function>()));
        op_table.get("MOPER").Functions.put("ADD", new Function("ADD",0));
        op_table.get("MOPER").Functions.put("SUB", new Function("SUB",1));
        op_table.get("MOPER").Functions.put("MUL", new Function("MUL",2));
        op_table.get("MOPER").Functions.put("DIV", new Function("DIV",3));
        op_table.get("MOPER").Functions.put("OR", new Function("OR",4));
        op_table.get("MOPER").Functions.put("AND", new Function("AND",5));
        op_table.get("MOPER").Functions.put("READN", new Function("READN",6));
        op_table.get("MOPER").Functions.put("READC", new Function("READC",6));
        op_table.get("MOPER").Functions.put("WRITEN", new Function("WRITEN",7));
        op_table.get("MOPER").Functions.put("WRITEC", new Function("WRITEC",7));


    }

    private static final Map<String,Directive> dir_table = new HashMap<String,Directive>();
    static{
        dir_table.put("START", new Directive("START", false, Format.RLABEL, Format.NMAX));
        dir_table.put("RESET", new Directive("RESET", true, Format.RLABEL, Format.MLABEL | Format.NMAX));
        dir_table.put("EQU", new Directive("EQU", Format.RLABEL, Format.NMAX | Format.MLABEL));
        dir_table.put("EQUE", new Directive("EQUE", Format.RLABEL, Format.EXPRESSION));
        dir_table.put("ENTRY", new Directive("ENTRY", Format.NONE, Format.RLABEL));
        dir_table.put("EXTRN", new Directive("EXTRN", Format.NONE, Format.RLABEL));
        dir_table.put("END", new Directive("END", Format.NONE, Format.RLABEL));
        dir_table.put("DAT", new Directive("DAT", true, Format.OLABEL, Format.DAT));
        dir_table.put("ADC", new Directive("ADC", true, Format.OLABEL, Format.MLABEL | Format.EXPRESSION | Format.NNNNNN));
        dir_table.put("ADCE", new Directive("ADCE", true, Format.OLABEL, Format.EXPRESSION));
        dir_table.put("NOP", new Directive("NOP", true, Format.NONE, Format.NONE));
    }

    public static Operator GetOperator(String s){
        return op_table.get(s.toUpperCase());
    }

    public static Directive GetDirective(String s){
        return dir_table.get(s.toUpperCase());
    }

    public static Type GetType(String s){
        
        if(op_table.containsKey(s.toUpperCase()))
        {
            return Type.OPERATOR;
        }
        else if(dir_table.containsKey(s.toUpperCase()))
        {
            return Type.DIRECTIVE;
        }
        else
        {
           return Type.ERROR;
        }
        
    }

}
